Skip to content

Fix phpstan/phpstan#3831: Comparison operation ">" between 0 and 0 is always false#5147

Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-wk7ev66
Closed

Fix phpstan/phpstan#3831: Comparison operation ">" between 0 and 0 is always false#5147
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-wk7ev66

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When a dynamic method call like $this->{'compileSection'}() was used, PHPStan did not invalidate tracked expression types on the called object. This caused false positives where properties assigned before the call (e.g., $this->footer = []) retained their narrowed types, leading to incorrect "always false" comparison errors like Comparison operation ">" between 0 and 0 is always false.

Changes

  • Added invalidateAllOnExpression() method to src/Analyser/MutatingScope.php — a targeted invalidation that clears tracked expression types (property fetches, method call results) on the given expression without clearing conditional type narrowing information
  • Modified src/Analyser/ExprHandler/MethodCallHandler.php to call invalidateAllOnExpression() on the receiver when processing a dynamic method call (where $expr->name is an Expr)
  • Added regression test in tests/PHPStan/Rules/Comparison/data/bug-3831.php and corresponding test method in NumberComparisonOperatorsConstantConditionRuleTest

Root cause

In MethodCallHandler, when the method name is dynamic ($this->{'name'}()), $methodReflection is null because PHPStan cannot resolve the method statically. The existing code only invalidated expressions in the $methodReflection !== null branch (when side effects were detected), so the else branch for unknown methods only added an implicit throw point without invalidating any tracked expression types.

The fix uses a new invalidateAllOnExpression() method instead of the existing invalidateExpression() to avoid clearing conditional type expressions — this prevents a regression where dynamic method calls on variables with conditional types (like $a->{$method}() with conditional parameter types) would lose their type narrowing.

Test

The regression test reproduces the original issue: a class with a property $footer that is set to [], followed by a dynamic method call that could modify it, and then a count($this->footer) > 0 check that should not produce a false positive.

Fixes phpstan/phpstan#3831

…method call

- Dynamic method calls like $this->{'compileSection'}() did not invalidate
  tracked expression types on the called object, causing properties set before
  the call (e.g. $this->footer = []) to retain their narrowed types
- Added invalidateAllOnExpression() to MutatingScope that clears tracked
  expression types (property fetches, method call results) without clearing
  conditional type narrowing
- New regression test in tests/PHPStan/Rules/Comparison/data/bug-3831.php
@staabm staabm deleted the create-pull-request/patch-wk7ev66 branch March 8, 2026 12:10
@staabm
Copy link
Copy Markdown
Contributor

staabm commented Mar 8, 2026

@VincentLanglet please make sure to remove branches on the phpstan-src repo after merging/closing PRs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants